/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.jitengine.metadataanalysis;

import com.inspur.edp.cdp.web.component.metadata.define.WebComponentMetadata;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.web.command.component.metadata.*;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.MetadataGetterParameter;
import com.inspur.edp.web.common.metadata.MetadataTypeEnum;
import com.inspur.edp.web.common.serialize.SerializeUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import com.inspur.edp.web.formmetadata.metadatamanager.CommandsMetadataManager;
import com.inspur.edp.web.formmetadata.metadatamanager.ComponentMetadataManager;

import java.util.*;

/**
 * 命令解析
 *
 * @author noah
 */

public class CommandsAnalysis {
    private ExecuteEnvironment executeEnvironment = ExecuteEnvironment.Design;
    private boolean isUpdradeTool = false;
    // 是否生成command.json 文件
    private boolean createCommandJsonFile = true;

    public CommandsAnalysis(ExecuteEnvironment executeEnvironment, boolean isUpdradeTool) {
        this.executeEnvironment = executeEnvironment;
        this.isUpdradeTool = isUpdradeTool;
    }

    public CommandsAnalysis(ExecuteEnvironment executeEnvironment, boolean isUpdradeTool, boolean createCommandJsonFile) {
        this.executeEnvironment = executeEnvironment;
        this.isUpdradeTool = isUpdradeTool;
        this.createCommandJsonFile = createCommandJsonFile;
    }


    public void resolveCommand(FormDOM json, String formMetadataName, String targetStoragePath,
                               HashMap<String, WebComponentMetadata> projectCmpList,
                               String webDevPath, String fileName) {
        HashMap<String, WebComponentMetadata> cmpList = new HashMap<>(8);
        CommandsMetadataManager cmdManager = new CommandsMetadataManager(this.executeEnvironment, this.isUpdradeTool);
        ComponentMetadataManager componentMetadataManager = new ComponentMetadataManager(this.executeEnvironment, this.isUpdradeTool);
        StringBuilder sbCommands = new StringBuilder();
        sbCommands.append("{");
        if (json.getModule() != null && json.getModule().getWebcmds() != null && json.getModule().getWebcmds().size() > 0) {
            sbCommands.append("\"commands\":{");
            int index = 0;
            WebCommandsMetadata metadata;
            String cmdId;
            for (HashMap<String, Object> c : json.getModule().getWebcmds()) {
                if (index > 0) {
                    sbCommands.append(",");
                }
                cmdId = c.get("id").toString();
                GspMetadata commandMetadata = cmdManager.getWebCommands(cmdId, StringUtility.getOrDefault(c.get("code"), "").toString(), StringUtility.getOrDefault(c.get("name"), ""), StringUtility.getOrDefault(c.get("nameSpace"), ""));
                metadata = (WebCommandsMetadata) commandMetadata.getContent();
                analysisComponentMetadata(metadata, cmpList, null, componentMetadataManager, projectCmpList);

                try {
                    analysisServiceRef(json, cmpList);
                } catch (Exception e) {
                    WebLogger.Instance.error(e);
                    return;
                }

                sbCommands.append("\"").append(cmdId).append("\":");
                sbCommands.append(cmdManager.serialize(metadata));
                index += 1;
            }
            sbCommands.append("}");

            if (cmpList.size() > 0) {
                sbCommands.append(",\"serviceList\":[");
                index = 0;

                Iterator iter = cmpList.entrySet().iterator();
                while (iter.hasNext()) {
                    HashMap.Entry entry = (HashMap.Entry) iter.next();
                    if (index > 0) {
                        sbCommands.append(",");
                    }
                    sbCommands.append(SerializeUtility.getInstance().serialize(entry.getValue(), false));
                    index += 1;
                }
                sbCommands.append("]");
            }
        }
        sbCommands.append("}");

        if (StringUtility.isNullOrEmpty(fileName)) {
            fileName = json.getModule().getCode().toLowerCase();
        }
        if (this.createCommandJsonFile) {
            cmdManager.saveMetadataFile(targetStoragePath, formMetadataName.toLowerCase() + ".command.json", sbCommands.toString());
        }

    }

    private void analysisComponentMetadata(WebCommandsMetadata metadata, HashMap<String, WebComponentMetadata> cmpList,
                                           List<BranchCommandItem> items, ComponentMetadataManager cmpManager,
                                           HashMap<String, WebComponentMetadata> projectCmpList) {
        if (items == null || items.size() == 0) {
            if (metadata.getCommands() != null && metadata.getCommands().size() > 0) {
                for (WebCommand webCommandItem : metadata.getCommands()) {
                    try {
                        analysisComponentCommandItemList(webCommandItem.getItems(), cmpList, cmpManager, projectCmpList);
                    } catch (Exception e) {
                        WebLogger.Instance.error(e);
                    }
                }
            }
        } else {
            for (BranchCommandItem branchCommandItem : items) {
                try {
                    analysisComponentCommandItemList(branchCommandItem.getItems(), cmpList, cmpManager, projectCmpList);
                } catch (Exception e) {
                    WebLogger.Instance.error(e);
                }
            }
        }
    }

    /// <summary>
    ///  解析commandItem
    /// </summary>
    /// <param name="commandItemList"></param>
    /// <param name="cmpList"></param>
    /// <param name="cmpManager"></param>
    private void analysisComponentCommandItemList(List<CommandItem> commandItemList, HashMap<String, WebComponentMetadata> cmpList,
                                                  ComponentMetadataManager cmpManager, HashMap<String, WebComponentMetadata> projectCmpList) throws Exception {
        if (commandItemList != null && commandItemList.size() > 0) {
            if (cmpManager == null) {
                cmpManager = new ComponentMetadataManager(this.executeEnvironment, this.isUpdradeTool);
            }
            WebComponentMetadata cmpMetadata = null;
            for (CommandItem item : commandItemList) {
                switch (item.getItemType()) {
                    case MethodRefer:
                        if (!cmpList.containsKey(((CmpMethodRefering) item).getComponentId())) {
                            if (!projectCmpList.containsKey(((CmpMethodRefering) item).getComponentId())) {
                                try {
                                    CmpMethodRefering cmpMethodReferingItem = (CmpMethodRefering) item;
                                    cmpMetadata = cmpManager.getComponentMetadata(cmpMethodReferingItem.getComponentId(), cmpMethodReferingItem.getCode(), cmpMethodReferingItem.getName(), cmpMethodReferingItem.getComponentPath());
                                    if (cmpMetadata == null) {
                                        throw new Exception("标识为'" + ((CmpMethodRefering) item).getComponentId() + "'的服务构件为null。");
                                    }
                                    WebLogger.Instance.info("Get WebCommands By Id:" + cmpMetadata.getId(), this.getClass().getName());
                                    cmpList.put(cmpMetadata.getId(), cmpMetadata);
                                    projectCmpList.put(cmpMetadata.getId(), cmpMetadata);
                                } catch (Exception ex) {
                                    throw new Exception("获取command元数据" + ((CmpMethodRefering) item).getComponentId() + "失败", ex);
                                }
                            } else {
                                cmpMetadata = projectCmpList.get(((CmpMethodRefering) item).getComponentId());
                                cmpList.put(cmpMetadata.getId(), cmpMetadata);
                            }
                        }
                        break;
                    case BranchCollection:
                        analysisComponentMetadata(null, cmpList, ((BranchCollectionCommandItem) item).getItems(), cmpManager, projectCmpList);
                        break;
                    default:
                        break;

                }
            }
        }
    }

    private void analysisServiceRef(FormDOM json, HashMap<String, WebComponentMetadata> cmpList) throws Exception {
        if (cmpList != null && cmpList.size() > 0) {

            Iterator iter = cmpList.entrySet().iterator();
            while (iter.hasNext()) {
                HashMap.Entry entry = (HashMap.Entry) iter.next();

                HashMap<String, Object> serviceRef = new HashMap<>();
                WebComponentMetadata component = (WebComponentMetadata) entry.getValue();
                if (component == null) {
                    throw new RuntimeException("不存在标识为'" + entry.getKey() + "'的服务构件");
                }

                if (!hasServiceReference(json.getModule().getServiceRefs(), component)) {
                    serviceRef.put("cmpId", component.getId());
                    serviceRef.put("name", component.getClassName());
                    String path = component.getSource();
                    if (path == null || path.isEmpty()) {
                        path = getTSFileName(component.getId());
                        component.setSource(path);
                    }
                    serviceRef.put("path", path);

                    serviceRef.put("isCommon", component.isCommon() ? "1" : "0");
                    serviceRef.put("alias", component.getClassName() + "1");

                    json.getModule().getServiceRefs().add(serviceRef);
                }
            }
        }
    }

    private boolean hasServiceReference(List<HashMap<String, Object>> serviceReferenceList, WebComponentMetadata component) {
        boolean flag = false;
        for (HashMap<String, Object> serviceRef : serviceReferenceList) {
            if (serviceRef.containsKey("name") && serviceRef.containsKey("path")) {
                if (serviceRef.get("name") == null) {
                    throw new RuntimeException("标识为'{" + serviceRef.get("cmpId") + "'的服务构件name属性不允许为null。");
                }

                if (serviceRef.get("path") == null) {
                    throw new RuntimeException("标识为'" + serviceRef.get("cmpId") + "'的服务构件path属性不允许为null。");
                }

                if (serviceRef.get("name").toString().equals(component.getClassName()) &&
                        serviceRef.get("path") == component.getSource()) {
                    flag = true;
                    break;
                }
            }
        }
        return flag;
    }

    /**
     * 获取ts文件路径和名称
     */
    private String getTSFileName(String cmpId) throws Exception {
        // 最理想的方式：路径来自webcmp构件路径，名称来自path（即元数据的Source属性）
        // 先用webcmp的文件名构造，后续可以考虑调整
        if (cmpId == null || cmpId.isEmpty()) {
            throw new Exception("获取ts文件名出错，对应的web构件id不能为空。");
        }

        CommandsMetadataManager manager = new CommandsMetadataManager(this.executeEnvironment, this.isUpdradeTool);
        GspMetadata metadata = manager.getMetadata(() -> {
            MetadataGetterParameter.GetterMetadataInfo getterMetadataInfo = new MetadataGetterParameter.GetterMetadataInfo();
            getterMetadataInfo.setId(cmpId);
            getterMetadataInfo.setMetadataType(MetadataTypeEnum.TS);
            return getterMetadataInfo;
        }, null, null);

        if (metadata == null) {
            throw new RuntimeException("load command metatdata is null,commandId is " + cmpId);
        }

        String path = metadata.getRelativePath();
        String cmpFileName = metadata.getHeader().getFileName();
        // 后缀为.webcmp
        int suffixIndex = cmpFileName.lastIndexOf(".webcmp");
        String fileName;
        if (suffixIndex > 0 && suffixIndex + 7 == cmpFileName.length()) {
            fileName = cmpFileName.substring(0, suffixIndex);
        } else if (cmpFileName.contains(metadata.getHeader().getCode())) {
            fileName = cmpFileName;
        } else {
            throw new Exception("获取ts文件名出错，web构件上获取的文件名不对。");
        }

        return path + "/" + fileName + ".ts";
    }
}
